#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
#include <semaphore.h>
#include <wait.h>
#include <signal.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <semaphore.h>
#include <sys/msg.h>
#include <sys/shm.h>
#include <sys/un.h>
#include <sys/epoll.h>
#include <iostream>
#include <map>
#include <list>
#include <vector>


#include "pack.h"
#include "dbhelper.h"


using namespace std;


void sendOnlineListToClient(int target_client);

bool client_handler(int client,dbHelper& db);

struct User{
	string username;
	FILE* fp;
	// 处理分包时需要以下3个数据
	Pack readed_pack; // 用来暂存已经读取到的部分协议包 以及 用来拼接未读取的协议包
	int readed_pack_size = 0; // 用来记录已读协议包的大小
	int unreaded_pack_size = 0; // 用来记录未读协议包的大小

	 // 处理4个字节的size 发生分包的可能性
	int readed_size = 0;
	int readed_size_size = 0;
	int unreaded_size_size = 0;
};

template <class T1,class T2>
class mymap:public map<T1,T2>{
public:
	int operator[](const string& name){
		for(const auto ele:*this){
			if(ele.second.username == name){
				return ele.first;
			}
		}
		return -1;
	}

	T2& operator[](const T1& key){
		return this->map<T1,T2>::operator[](key);
	}
};

mymap<int,User> m;// 键为：客户端套接字，值为 User 对象
// 这个map的作用为：存储所有在线用户的套接字信息和用户的相关信息



int main(int argc, const char *argv[])
{
	if(argc < 2){
		printf("请末端口号\n");
		return 1;
	}

	dbHelper db("user.db");

	short port = atoi(argv[1]);
	// "abc123" -> 0
	int server = socket(AF_INET,SOCK_STREAM,0);

	struct sockaddr_in addr = {0};
	addr.sin_family = AF_INET;	
	addr.sin_port = htons(port);
	addr.sin_addr.s_addr = inet_addr("0.0.0.0");

	if(bind(server,(struct sockaddr*)&addr,sizeof(addr)) == -1){
		perror("bind");
		return 1;
	}

	listen(server,10);

	// 创建epoll监视列表
	int epfd = epoll_create1(EPOLL_CLOEXEC);

	// 将 0 和 server 添加进入监视列表
	//struct epoll_event epoll_stdin = {.events = EPOLLIN ,data:{fd:server}};
	struct epoll_event epoll_stdin = {events : EPOLLIN , data:{fd:0}};
	struct epoll_event epoll_server = {events : EPOLLIN , data:{fd : server}};

	epoll_ctl(epfd,EPOLL_CTL_ADD,0,&epoll_stdin);
	epoll_ctl(epfd,EPOLL_CTL_ADD,server,&epoll_server);
	int eplen = 2;


	while(1){
		// 提前准备一个激活列表
		struct epoll_event list[20] = {0};
		int count = epoll_wait(epfd,list,eplen,-1);

		for(int i=0;i<count;i++){
			// list里面全都是激活的描述符，最多判断一下 ，以何种方式激活的

			// 将激活的具体描述符单独取出
			int fd = list[i].data.fd;
			if(fd == 0){
				char buf[1024] = "";
				scanf("%s",buf);
				getchar();
				printf("键盘输入数据:%s\n",buf);
				continue;
			}

			if(fd == server){
				printf("有客户端连接\n");
				struct sockaddr_in client_addr = {0};
				socklen_t client_len = sizeof(client_addr);
				int client = accept(server,(struct sockaddr*)&client_addr,&client_len);
				printf("新连接的客户端的ip = %s\n",inet_ntoa(client_addr.sin_addr));
				printf("新连接的客户端的port = %d\n",ntohs(client_addr.sin_port));

				struct epoll_event epoll_client = {events : EPOLLIN , data:{fd : client}};
				epoll_ctl(epfd,EPOLL_CTL_ADD,client,&epoll_client);
				eplen ++;
	

				continue;
			}

			// 剩下的都是客户端描述符
			bool res = client_handler(fd,db);// 这个函数用来写处理客户端的逻辑
			if(res == false){
				epoll_ctl(epfd,EPOLL_CTL_DEL,fd,nullptr);
				m.erase(fd);
			}
		}
		
	}
	return 0;
}

bool client_handler(int client,dbHelper& db){
	while(1){
		int size = 0;
		int res = 0;
		Pack pack;
	
		if(m[client].unreaded_pack_size != 0){
			res=recv(client,(char*)&m[client].readed_pack + m[client].readed_pack_size,m[client].unreaded_pack_size,MSG_DONTWAIT);
			if(res != m[client].unreaded_pack_size){
				// 这里只要更新下已读大小和未读大小，保证下一次进入当前分支的时候，能够根据已读大小和未读大小，读取对应的数据以及存放到对应的地方
				m[client].readed_pack_size += res;
				m[client].unreaded_pack_size -= res;
				break;
			}

			pack = m[client].readed_pack;
			// 处理完分包记得 unreaded_pack_size = 0;
			m[client].unreaded_pack_size = 0;
		}else{

			if(m[client].unreaded_size_size != 0){
				recv(client,(char*)&m[client].readed_size + m[client].readed_size_size,m[client].unreaded_size_size,MSG_DONTWAIT);
				size = m[client].readed_size;
				m[client].unreaded_size_size = 0;
			}else{

				// else 部分为正常读取数据：先读4字节，再读剩余字节数，可能处理分包
				res = recv(client,(char*)&size,4,MSG_DONTWAIT);
				if(res == -1){
					//cout << "退出循环" << endl;
					return true;
				}else if(res == 0){
					cout << "4字节recv结束" << endl;
					return false;
				}

				if(res != 4){
					memcpy(&m[client].readed_size,&size,m[client].readed_size_size);
					m[client].readed_size_size = res;
					m[client].unreaded_size_size = 4 - res;
					break;
				}
			}


			res = recv(client,(char*)&pack+4,size-4,MSG_DONTWAIT);
			if(res == 0){
				cout << "size-4字节recv结束" << endl;
				return false;
			}else if(res == -1){
				return true;
			}
			pack.setSize(size);


			if(res != size-4){
				// 如果实际读取到的字节数 != 想要读取的字节数
				// 说明发生了分包
				cout << "发生分包" << endl;
				memcpy(&m[client].readed_pack,&pack,res+4);// 将已经读取的协议包缓存在每个客户端专属的缓存区 readed_pack 里面
				m[client].readed_pack_size = res + 4;
				m[client].unreaded_pack_size = size - m[client].readed_pack_size;
				break;
			}
		}



		switch(pack.getType()){
			case TYPE_REGIST:{
				vector<string> list = pack.readAll();
				string name = list[0];
				string pswd = list[1];
				bool res = db.regist(name,pswd);
				if(res == true){
					pack.setBack(BACK_SUCCESS);
				}else{
					pack.setBack(BACK_ERR);
				}
				pack >> client; // 将协议包回给客户端
				break;
			}
			case TYPE_LOGIN:{
				vector<string> list = pack.readAll();
				string name = list[0];
				string pswd = list[1];
				bool res = db.login(name,pswd);
				// 在TYPE_LOGIN的case中，登录成功后添加
				if(res == true) {
					pack.setBack(BACK_SUCCESS);
					m[client].username = name;
					
					// 发送用户上线通知
					Pack onlinePack;
					onlinePack.setType(TYPE_USER_ONLINE);
					onlinePack >> client;  // 触发服务端广播在线列表
				}else{
					pack.setBack(BACK_ERR);
				}
				pack >> client; // 将协议包回给客户端
				break;
			}
			// 添加处理TYPE_MESSAGE的case分支
			case TYPE_MESSAGE:{
				vector<string> list = pack.readAll();
				if (list.size() >= 4) {
					string sender = list[0];
					string targetUser = list[1];
					string message = list[2];
					string time = list[3];

					// 打印接收到的消息
					cout << "收到消息：" << sender << " -> " << targetUser << ": " << message << " (" << time << ")" << endl;

					// 创建新的消息包转发
					Pack forwardPack;
					forwardPack.setType(TYPE_MESSAGE);
					forwardPack << sender << targetUser << message << time;

					// 转发给目标用户
					if (targetUser == "all") {
						// 广播消息给所有在线用户
						for (auto& ele : m) {
							if (ele.first != client) {
								forwardPack >> ele.first;
							}
						}
					} else {
						// 查找目标用户的socket
						int targetClient = m[targetUser];
						if (targetClient != -1) {
							forwardPack >> targetClient;
						} else {
							// 目标用户不在线，发送错误消息给发送者
							Pack errorPack;
							errorPack.setType(TYPE_MESSAGE_ACK);
							errorPack.setBack(BACK_USER_NOT_ONLINE);
							errorPack << "目标用户不在线";
							errorPack >> client;
						}
					}
				} else {
					cout << "无效的消息包结构，列表大小：" << list.size() << endl;
				}
				break;
			}
			case TYPE_FILE_UPLOAD_REQUEST:{
				cout << "文件上传请求" << endl;
				vector<string> list = pack.readAll();
				string filename = list[0];
				m[client].fp = fopen(filename.data(),"w");
				if(m[client].fp == nullptr){ // 检查文件打开是否成功
					perror("fopen failed");
					pack.setBack(BACK_ERR);
					pack >> client; // 通知客户端上传失败
				}
				break;
			}
			case TYPE_FILE_UPLOADING:{
				cout << "文件上传中" << endl;
				char buf[4096] = "";
				pack.readAll(buf,pack.size()-12);
				// 到此为止，客户端发来的一部分的文件内容，保存到了buf里面
				fwrite(buf,1,pack.size()-12,m[client].fp);
				break;
			}
			case TYPE_FILE_UPLOAD_END:{
				cout << "文件上传完成" << endl;
				fclose(m[client].fp);
				break;
			}
			case TYPE_SENDING_CAMERA:
			case TYPE_SEND_CAMERA_END:{
				// 服务器只做一个事情：将接受到的所有图片内容，转发给其他所有客户端
				cout << "转发camera图片..." << endl;
				for(auto& ele:m){
					if(ele.first != client){
						pack >> ele.first;
					}
				}
				break;
			}

			// 在这里新写 2 个case，用来转发桌面画面
			case TYPE_SENDING_DESKTOP:
			case TYPE_SEND_DESKTOP_END:{
				cout<<"转发桌面画面.."<<endl;
				for(auto& ele:m){
					if(ele.first!=client){
						pack>>ele.first;
					}
				}
				break;
			}
			// 在 client_handler 的 switch 语句中添加以下 case 分支
			case TYPE_SCHEDULE_SAVE: {
				vector<string> list = pack.readAll();
				if (list.size() < 2) {
					pack.setBack(BACK_ERR);
					break;
				}
				
				string date = list[0];
				string event = list[1];
				bool res = db.saveSchedule(m[client].username, date, event);
				
				pack.setBack(res ? BACK_SUCCESS : BACK_ERR);
				pack >> client;
				break;
			}

			case TYPE_SCHEDULE_UPDATE: {
				vector<string> list = pack.readAll();
				if (list.size() < 3) {
					pack.setBack(BACK_ERR);
					break;
				}
				
				string oldDate = list[0];
				string newDate = list[1];
				string newEvent = list[2];
				bool res = db.updateSchedule(m[client].username, oldDate, newDate, newEvent);
				
				pack.setBack(res ? BACK_SUCCESS : BACK_ERR);
				pack >> client;
				break;
			}

			case TYPE_SCHEDULE_DELETE: {
				vector<string> list = pack.readAll();
				if (list.empty()) {
					pack.setBack(BACK_ERR);
					break;
				}
				
				string date = list[0];
				bool res = db.deleteSchedule(m[client].username, date);
				
				pack.setBack(res ? BACK_SUCCESS : BACK_ERR);
				pack >> client;
				break;
			}

			case TYPE_SCHEDULE_QUERY:
			case TYPE_SCHEDULE_QUERY_ALL: {
				vector<string> list = pack.readAll();
				string date = (pack.getType() == TYPE_SCHEDULE_QUERY && !list.empty()) ? list[0] : "";
				
				cout << "查询日程请求，用户名: " << m[client].username 
					<< ", 日期: " << (date.empty() ? "所有" : date) << endl;
				
				auto schedules = db.querySchedules(m[client].username, date);
				
				cout << "查询到 " << schedules.size() << " 条日程" << endl;
				
				Pack response;
				response.setType(pack.getType());
				response.setBack(BACK_SUCCESS);
				
				for (const auto& [sdate, event] : schedules) {
					response << sdate << event;
					cout << "打包数据:" << sdate.c_str() << event.c_str(); // 添加调试输出
				}
				
				response >> client;
				break;
			}
			// 在epoll_server.cpp中添加消息处理case
			// 修改 TYPE_USER_ONLINE 分支
			case TYPE_USER_ONLINE: {
			    // 用户上线后通知所有在线用户
			    cout<<"用户上线"<<endl;
			    string username = m[client].username;
			    
			    // 为每个客户端单独发送在线列表，排除其自己
			    for(auto& ele : m) {
			        Pack notifyPack;
			        notifyPack.setType(TYPE_ONLINE_LIST);
			        
			        // 打包所有在线用户名，但排除当前客户端自己
			        for(auto& user : m) {
			            if (!user.second.username.empty() && user.first != ele.first) {
			                notifyPack << user.second.username;
			            }
			        }
			        
			        notifyPack >> ele.first;
			    }
			    break;
			}
			// 修改 TYPE_USER_OFFLINE 分支
			case TYPE_USER_OFFLINE: {
			    // 处理用户下线逻辑（通常在客户端断开连接时触发）
			    cout<<"用户下线"<<endl;
			    string offlineUser = m[client].username;
			    m.erase(client); // 从在线列表移除
			    
			    // 通知所有在线用户更新列表
			    for(auto& ele : m) {
			        Pack notifyPack;
			        notifyPack.setType(TYPE_ONLINE_LIST);
			        
			        // 打包所有在线用户名，但排除当前客户端自己
			        for(auto& user : m) {
			            if (!user.second.username.empty() && user.first != ele.first) {
			                notifyPack << user.second.username;
			            }
			        }
			        
			        notifyPack >> ele.first;
			    }
			    break;
			}
			// 删除从这里开始的重复TYPE_MESSAGE分支
			/*
			case TYPE_MESSAGE:
			{
			    std::vector<std::string> list = pack.readAll();
			    if (list.size() < 4) {
			        pack.setBack(BACK_ERR);
			        pack >> client;
			        break;
			    }
			
			    std::string sender = list[0];
			    std::string targetUser = list[1];
			    std::string message = list[2];
			    std::string time = list[3];
			
			    // 查找目标用户
			    int target_fd = m[targetUser]; // 使用已定义的mymap操作符
			    if (target_fd == -1) {
			        // 目标用户不在线
			        // 注意：如果BACK_USER_NOT_ONLINE未在pack.h中定义，需要先添加此枚举值
			        // 若暂时没有定义，可以先用BACK_ERR代替
			        pack.setBack(BACK_ERR);
			        pack >> client;
			    } else {
			        // 转发消息
			        Pack sendPack;
			        sendPack.setType(TYPE_MESSAGE);
			        sendPack << sender << targetUser << message << time;
			        sendPack >> target_fd;

			        // 向发送者确认消息已发送
			        pack.setBack(BACK_SUCCESS);
			        pack >> client;
			    }
			    break;
			}
			*/
			default:
				break;
		}

		return true;
	}
}

// 在epoll_server.cpp中添加该函数，可放在client_handler函数上方
// 修改 sendOnlineListToClient 函数
void sendOnlineListToClient(int target_client) {
    // 创建协议包并设置类型为在线列表同步
    Pack pack;
    pack.setType(TYPE_ONLINE_LIST);
    pack.setBack(BACK_SUCCESS);

    // 遍历在线用户map，收集所有用户名，但排除目标客户端自己
    for (const auto& pair : m) {
        // 只添加已登录的用户（username不为空）且不是目标客户端自己
        if (!pair.second.username.empty() && pair.first != target_client) {
            pack << pair.second.username;
        }
    }

    // 发送在线列表到目标客户端
    pack >> target_client;
    cout << "已向客户端 " << target_client << " 发送在线用户列表" << endl;
}